# -*- coding: utf-8 -*-

import os.path, sys

newpath = os.path.abspath('../..')
if not newpath in sys.path:
    sys.path.append(newpath)

from smally.uri import URL

class IncompleteRequest(Exception):
    pass

class IncompleteChunkedRequest(IncompleteRequest):
    pass

class ContinueRequest(Exception):
    pass

class Redirect(Exception):
    pass

class Request:
    
    def __init__(self, source='', url=URL(), method='HEAD',
                 version='HTTP/1.1', content='', headers={}):
        self.url = url
        self.method = method
        self.version = version
        self.content = content
        self.headers = headers
        self.source = source
        if source:
            self.feed(source)
            
    def feed(self, source):
        # Not useful yet, and hard to maintain.
        pass
    
    def __str__(self):
        self.headers['Host'] = ['{}:{}'.format(self.url.domain,
                                              self.url.port)]
        self.contentlength = len(bytes(self.content, 'utf-8'))
        if self.contentlength:
            self.headers['Content-Length'] = [len(bytes(self.content,
                                                       'utf-8'))]
        elif 'Content-Length' in self.headers:
            del self.headers['Content-Length']
        headersstring = ''
        if self.headers:
            for name, values in self.headers.items():
                for value in values:
                    headersstring += '{}: {}\r\n'.format(name, value)
        pathstring = '/' + '/'.join(self.url.path)
        request_string = '{} {} {}\r\n{}\r\n{}\r\n\r\n'.format(\
            self.method, pathstring, self.version, headersstring,
            self.content)
        return request_string

    def __iter__(self):
        self.index = 0
        return self

    def __next__(self):
        if self.index < len(str(self)):
            return ord(str(self)[self.index])
        else:
            raise StopIteration

class Response:
    def __init__(self, source='', version='HTTP/1.1',
                 status=[100, 'Continue'], headers={}, content='',
                 stream=False):
        self.version = version
        self.status = status
        self.headers = headers
        self.content = content
        self.source = source
        self.step = 0
        self.curdata = ''
        self.chunked = False
        self.contentlen = 0
        self.newstep = False
        self.allchunks = False
        self.stream = stream
        self.feed()
            
    def feed(self, last=False):
        for char in self.source:
            if self.step == 0:
                # Get version
                if char == ' ':
                    self.version = self.curdata
                    self.curdata = ''
                    self.step = 1
                else:
                    self.curdata += char
            elif self.step == 1:
                # Get status code
                if char == ' ':
                    if self.curdata == '100':
                        raise ContinueRequest
                    self.status[0] = int(self.curdata)
                    self.curdata = ''
                    self.step = 2
                else:
                    self.curdata += char
            elif self.step == 2:
                # Get status message
                if char == '\r':
                    self.status[1] = self.curdata
                    self.curdata = ''
                elif char == '\n':
                    self.step = 3
                else:
                    self.curdata += char
            elif self.step == 3:
                # Get headers name
                if char == '\r' and not self.curdata:
                    continue
                elif char == '\n' and not self.curdata:
                    if self.chunked:
                        self.step = 6
                    else:
                        self.step = 5
                elif char == ':':
                    self.curdata = [self.curdata, '']
                    self.step = 4
                else:
                    self.curdata += char
            elif self.step == 4:
                # Get headers value
                if char == '\r':
                    header = self.curdata[0].strip()
                    value = self.curdata[1].strip()
                    if header in self.headers:
                        self.headers[header].append(value)
                    else:
                        self.headers[header] = [value]
                    self.curdata = ''
                    if header == 'Location' and\
                       self.status[0] in [301, 302, 303]:
                        raise Redirect(value)
                    if (header, value) ==\
                       ('Transfer-Encoding', 'self.chunked'):
                        self.chunked = True
                elif char == '\n':
                    self.step = 3
                else:
                    self.curdata[1] += char
            elif self.step == 5:
                # Get content
                self.content += char
            elif self.step == 6:
                if char == '\r' and not self.curdata:
                    break
                elif char in [';', '\r']:
                    self.newstep = True
                    chunklen = int(self.curdata, 16)
                    if chunklen:
                        self.contentlen += chunklen
                    else:
                        self.allchunks = True
                        self.step = 3
                    self.curdata = ''
                elif char == '\n' and self.newstep:
                    self.newstep = False
                    self.step = 7
                else:
                    self.curdata += char
            elif self.step == 7:
                if char == '\r':
                    self.content += self.curdata
                    self.newstep = True
                elif char == '\n' and self.newstep:
                    self.newstep = False
                    self.curdata = ''
                    self.step = 6
                else:
                    self.curdata += char
        if last or not self.stream and self.chunked and not self.allchunks:
            raise Incompleteself.chunkedRequest
        elif last or not self.stream and not self.chunked and\
             int(self.headers.get('Content-Length', [0])[0]) !=\
             len(self.content):
                raise IncompleteRequest
        if last or not self.stream:
            self.version = version
            self.status = status
            self.headers = headers
            self.content = content
            self.source = source
            self.step = 0
            self.curdata = ''
            self.chunked = False
            self.contentlen = 0
            self.newstep = False
            self.allchunks = False
            self.stream = False
                    
    def __str__(self):
        # Not useful yet.
        return 'Not implemented.'
